package task

import (
	"context"
	"fmt"
	"sync"
	"time"

	"gitee.com/fu-ce-wei/pi-mapper/internal/config"
	"gitee.com/fu-ce-wei/pi-mapper/internal/service"
	"gitee.com/fu-ce-wei/pi-mapper/pkg/logger"
	"go.uber.org/zap"
)

// Scheduler 定时任务调度器
type Scheduler struct {
	scanService      service.ScanService
	imageScanService service.ImageScanService
	config           *config.Config
	logger           *logger.Logger

	mu         sync.Mutex
	running    bool
	cancelFunc context.CancelFunc
	wg         sync.WaitGroup

	lastRunTime      time.Time
	nextRunTime      time.Time
	lastImageRunTime time.Time
	nextImageRunTime time.Time
}

// NewScheduler 创建新的调度器实例
func NewScheduler(scanService service.ScanService, imageScanService service.ImageScanService, config *config.Config, logger *logger.Logger) *Scheduler {
	return &Scheduler{
		scanService:      scanService,
		imageScanService: imageScanService,
		config:           config,
		logger:           logger,
	}
}

// Start 启动定时任务调度器
func (s *Scheduler) Start(ctx context.Context) error {
	s.mu.Lock()
	if s.running {
		s.mu.Unlock()
		return fmt.Errorf("调度器已在运行中")
	}
	s.running = true
	s.mu.Unlock()

	// 创建可取消的上下文
	ctx, cancel := context.WithCancel(ctx)
	s.cancelFunc = cancel

	s.logger.Info("启动定时任务调度器")

	// 如果配置要求在启动时立即执行一次扫描
	if s.config.Scheduler.ImmediateRun {
		s.logger.Info("启动时立即执行一次视频扫描")
		go s.executeVideoTask(ctx)

		s.logger.Info("启动时立即执行一次图片扫描")
		go s.executeImageTask(ctx)
	}

	// 启动定时执行
	s.wg.Add(1)
	go func() {
		defer s.wg.Done()
		s.runScheduler(ctx)
	}()

	return nil
}

// Stop 停止定时任务调度器
func (s *Scheduler) Stop() {
	s.mu.Lock()
	if !s.running {
		s.mu.Unlock()
		return
	}
	s.running = false

	if s.cancelFunc != nil {
		s.cancelFunc()
	}
	s.mu.Unlock()

	// 等待所有goroutine完成
	done := make(chan struct{})
	go func() {
		s.wg.Wait()
		close(done)
	}()

	// 设置超时时间
	select {
	case <-done:
		s.logger.Info("定时任务调度器已停止")
	case <-time.After(30 * time.Second):
		s.logger.Warn("停止调度器超时，强制退出")
	}
}

// IsRunning 检查调度器是否正在运行
func (s *Scheduler) IsRunning() bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.running
}

// GetStatus 获取调度器状态
func (s *Scheduler) GetStatus() map[string]interface{} {
	s.mu.Lock()
	defer s.mu.Unlock()

	return map[string]interface{}{
		"running":             s.running,
		"last_run_time":       s.lastRunTime,
		"next_run_time":       s.nextRunTime,
		"last_image_run_time": s.lastImageRunTime,
		"next_image_run_time": s.nextImageRunTime,
	}
}

// runScheduler 运行调度器主循环
func (s *Scheduler) runScheduler(ctx context.Context) {
	interval, err := s.config.GetIntervalDuration()
	if err != nil {
		s.logger.Error("获取调度间隔失败，使用默认间隔6小时", zap.Error(err))
		interval = 6 * time.Hour
	}

	s.logger.Info("定时任务调度器已启动",
		zap.Duration("interval", interval),
		zap.String("interval_str", interval.String()),
	)

	// 创建两个定时器：一个用于视频扫描，一个用于图片扫描
	videoTicker := time.NewTicker(interval)
	imageTicker := time.NewTicker(interval)
	defer videoTicker.Stop()
	defer imageTicker.Stop()

	for {
		select {
		case <-ctx.Done():
			s.logger.Info("调度器收到停止信号")
			return
		case <-videoTicker.C:
			s.logger.Info("视频定时任务触发，开始执行扫描")
			go s.executeVideoTask(ctx)
		case <-imageTicker.C:
			s.logger.Info("图片定时任务触发，开始执行扫描")
			go s.executeImageTask(ctx)
		}
	}
}

// executeVideoTask 执行视频扫描任务
func (s *Scheduler) executeVideoTask(ctx context.Context) {
	// 创建任务上下文，设置超时
	timeout, err := s.config.GetTimeoutDuration()
	if err != nil {
		s.logger.Error("获取任务超时时间失败，使用默认超时30分钟", zap.Error(err))
		timeout = 30 * time.Minute
	}

	taskCtx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	s.logger.Info("开始执行视频扫描任务",
		zap.String("directory", s.config.Scanner.RootPaths[0]),
		zap.Duration("timeout", timeout),
	)

	// 记录开始时间
	startTime := time.Now()
	s.lastRunTime = startTime

	// 执行视频扫描任务
	result, err := s.scanService.ScanDirectory(taskCtx)
	if err != nil {
		s.logger.Error("视频扫描任务执行失败",
			zap.Error(err),
			zap.Duration("duration", time.Since(startTime)),
		)
		return
	}

	// 记录完成信息
	s.logger.Info("视频扫描任务执行完成",
		zap.Int("total_files", result.TotalFiles),
		zap.Int("processed_files", result.ProcessedFiles),
		zap.Int("added_files", result.AddedFiles),
		zap.Duration("duration", result.Duration),
	)

	// 更新下次运行时间
	interval, _ := s.config.GetIntervalDuration()
	s.nextRunTime = time.Now().Add(interval)
}

// executeImageTask 执行图片扫描任务
func (s *Scheduler) executeImageTask(ctx context.Context) {
	// 创建任务上下文，设置超时
	timeout, err := s.config.GetTimeoutDuration()
	if err != nil {
		s.logger.Error("获取任务超时时间失败，使用默认超时30分钟", zap.Error(err))
		timeout = 30 * time.Minute
	}

	taskCtx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	s.logger.Info("开始执行图片扫描任务",
		zap.String("directory", s.config.Image.RootPath),
		zap.Duration("timeout", timeout),
	)

	// 记录开始时间
	startTime := time.Now()
	s.lastImageRunTime = startTime

	// 执行图片扫描任务
	err = s.imageScanService.ScanImageFiles(taskCtx)
	if err != nil {
		s.logger.Error("图片扫描任务执行失败",
			zap.Error(err),
			zap.Duration("duration", time.Since(startTime)),
		)
		return
	}

	// 记录完成信息
	s.logger.Info("图片扫描任务执行完成",
		zap.Duration("duration", time.Since(startTime)),
	)

	// 更新下次运行时间
	interval, _ := s.config.GetIntervalDuration()
	s.nextImageRunTime = time.Now().Add(interval)
}

// TriggerManualScan 手动触发视频扫描
func (s *Scheduler) TriggerManualScan(ctx context.Context) error {
	s.mu.Lock()
	if !s.running {
		s.mu.Unlock()
		return fmt.Errorf("调度器未运行")
	}
	s.mu.Unlock()

	s.logger.Info("手动触发视频扫描任务")
	go s.executeVideoTask(ctx)
	return nil
}

// TriggerManualImageScan 手动触发图片扫描
func (s *Scheduler) TriggerManualImageScan(ctx context.Context) error {
	s.mu.Lock()
	if !s.running {
		s.mu.Unlock()
		return fmt.Errorf("调度器未运行")
	}
	s.mu.Unlock()

	s.logger.Info("手动触发图片扫描任务")
	go s.executeImageTask(ctx)
	return nil
}

// GetNextRunTime 获取下次视频运行时间
func (s *Scheduler) GetNextRunTime() time.Time {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.nextRunTime
}

// GetLastRunTime 获取上次视频运行时间
func (s *Scheduler) GetLastRunTime() time.Time {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.lastRunTime
}

// GetNextImageRunTime 获取下次图片运行时间
func (s *Scheduler) GetNextImageRunTime() time.Time {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.nextImageRunTime
}

// GetLastImageRunTime 获取上次图片运行时间
func (s *Scheduler) GetLastImageRunTime() time.Time {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.lastImageRunTime
}

// Wait 等待调度器停止
func (s *Scheduler) Wait() {
	s.wg.Wait()
}
